# plugs/alarm.py
#
#

""" handle txt alarms """

__copyright__ = 'this file is in the public domain'

from gozerbot.generic import handle_exception, rlog, strtotime, striptime
from gozerbot.persist import Persist
from gozerbot.commands import cmnds
from gozerbot.examples import examples
from gozerbot.nextid import nextid
from gozerbot.fleet import fleet
from gozerbot.datadir import datadir
from gozerbot.plughelp import plughelp
from gozerbot.aliases import aliases
from gozerbot.gozertest import gtest

import gozerbot.thr as thr
import time

plughelp.add('alarm', 'the alarm plugin allows for alarms which /msg the user\
giving the command at a certain time or at number of seconds from now')

class Alarmitem(object):

    """ item holding alarm data """

    def __init__(self, botname, i, nick, ttime, txt):
        self.botname = botname
        self.idnr = i
        self.nick = nick
        self.time = ttime
        self.txt = txt

    def __str__(self):
        result = "%s %s %s %s %s" % (self.botname, self.idnr, self.nick, \
self.time, self.txt)
        return result

class Alarms(Persist):

    """ class that holds the alarms """

    def __init__(self, fname):
        Persist.__init__(self, fname)
        if not self.data:
            self.data = []
        self.run = 1

    def size(self):
        """ return number of alarms """
        return len(self.data)

    def bynick(self, nick):
        """ get alarms by nick """
        nick = nick.lower()
        result = []
        for i in self.data:
            if i.nick == nick:
                result.append(i)
        return result

    def alarmsay(self, item):
        """ say alarm txt """
        bot = fleet.byname(item.botname)
        if bot:
            bot.say(item.nick, item.txt)
            return 1

    def add(self, botname, nick, ttime, txt):
        """ add alarm """
        nick = nick.lower()
        nrid = nextid.next('alarms')
        self.data.append(Alarmitem(botname, nrid, nick, ttime, txt))
        self.save()
        return nrid

    def delete(self, idnr):
        """ delete alarmnr """
        for i in range(len(self.data)-1, -1, -1):
            if self.data[i].idnr == idnr:
                del self.data[i]
                self.save()
                return 1

    def check(self):
        """ check if alarms should ring """
        rlog(0, 'alarms', 'starting alarms checker')
        # poll data every second
        while self.run:
            time.sleep(1)
            if not self.run:
                break
            ttime = time.time()
            for i in self.data:
                # see if its time to say txt
                if ttime > i.time:
                    self.alarmsay(i)
                    self.delete(i.idnr)

alarms = Alarms(datadir + '/alarms')

def init():
    """ run after reload """
    thr.start_new_thread(alarms.check, ())
    return 1
    
def shutdown():
    """ run before reload """
    alarms.run = 0
    return 1

def handle_alarmadd(bot, ievent):
    """ alarm <txt-with-time> | <+delta> <txt> .. add an alarm """
    if not ievent.rest:
        ievent.reply('alarm <txt-with-time> or alarm <+delta> <txt>')
        return
    else:
        alarmtxt = ievent.rest
    # see if alarm time is given as delta aka starts with +
    if alarmtxt[0] == '+':
        try:
            sec = int(ievent.args[0][1:]) # get nr of seconds to sleep
            ttime = time.time() + sec
            # check for time overflow
            if ttime > 2**31:
                ievent.reply("time overflow")
                return
            if ttime < 0:
                ievent.reply("time underflow")
                return
            # add alarm 
            nrid = alarms.add(bot.name, ievent.nick, ttime, \
' '.join(ievent.args[1:]))
            ievent.reply("alarm %s set at %s" % (nrid, time.ctime(ttime)))
            return
        except Exception, ex:
            handle_exception(ievent)
            return
    # see if we can determine time from txt
    alarmtime = strtotime(alarmtxt)
    if not alarmtime:
        ievent.reply("can't detect time")
        return
    # check if alarm txt is provided
    txt = striptime(alarmtxt).strip()
    if not txt:
        ievent.reply('i need txt to remind you')
        return
    if time.time() > alarmtime:
        ievent.reply("we are already past %s" % time.ctime(alarmtime))
        return
    # add alarm
    nrid = alarms.add(bot.name, ievent.nick, alarmtime, txt)
    ievent.reply("alarm %s set at %s" % (nrid, time.ctime(alarmtime)))

cmnds.add('alarm', handle_alarmadd, 'USER')
examples.add('alarm', 'say txt at a specific time or time diff', \
'1) alarm 12:00 lunchtime 2) alarm +180 egg ready')
gtest.add('alarm', 'alarm')
gtest.add('alarm 12:00 lunch', 'alarm set')
gtest.add('alarm +180 egg', 'alarm set')

def handle_alarmlist(bot, ievent):
    """ alarm-list .. list all alarms """
    result = ""
    for i in alarms.data:
        result += "%s) %s: %s - %s " % (i.idnr, i.nick, time.ctime(i.time), \
i.txt)
    if result:
        ievent.reply(result)
    else:
        ievent.reply('no alarms')

cmnds.add('alarm-list', handle_alarmlist, 'OPER')
examples.add('alarm-list', 'list current alarms', 'alarm-list')
gtest.add('alarm-list', '\d+\)')

def handle_myalarmslist(bot, ievent):
    """ alarm-mylist .. show alarms of user giving the command """
    result = ""
    nick = ievent.nick.lower()
    for i in alarms.data:
        if i.nick == nick:
            result += "%s) %s - %s " % (i.idnr, time.ctime(i.time), i.txt)
    if result:
        ievent.reply(result)
    else:
        ievent.reply('no alarms')

cmnds.add('alarm-mylist', handle_myalarmslist, 'USER')
examples.add('alarm-mylist', 'list alarms of user giving the commands', \
'alarm-mylist')
gtest.add('alarm-mylist','\d+\)')
aliases.data['myalarms'] = 'alarm-mylist'

def handle_alarmdel(bot, ievent):
    """ alarm-del <nr> .. delete alarm """
    try:
        alarmnr = int(ievent.args[0])
    except IndexError:
        ievent.missing('<nr>')
        return
    except ValueError:
        ievent.reply('argument needs to be an integer')
        return
    if alarms.delete(alarmnr):
        ievent.reply('alarm with id %s deleted' % alarmnr)
    else:
        ievent.reply('failed to delete alarm with id %s' % alarmnr)

cmnds.add('alarm-del', handle_alarmdel, 'OPER')
examples.add('alarm-del', 'delete alarm with id <nr>', 'alarm-del 1')
gtest.add('alarm-del', 'nr')
gtest.add('alarm-del bla', 'argument')
gtest.add('alarm-del 1', 'deleted')
